#pragma once
#include <iostream>
#include <queue>
#include <pthread.h>


namespace ns_tp
{
    template<class T, size_t N = 5>
    class thread_pool
    {
    private:
        static void* Rountine(void* args)
        {
            pthread_detach(pthread_self());
            thread_pool* self = (thread_pool*) args;

            while (1)
            {
                self->lock();
                while (self->_q.empty()) self->wait();
                T t;
                self->pop(&t);
                self->unlock();
                // t.run();
                t();
            }
        }

        void lock()
        {
            pthread_mutex_lock(&_mtx);
        }

        void unlock()
        {
            pthread_mutex_unlock(&_mtx);
        }   

        void wait()
        {
            pthread_cond_wait(&_cond, &_mtx);
        }

        void signal()
        {
            pthread_cond_signal(&_cond);
        }

        void broadcast()
        {
            pthread_cond_broadcast(&_cond);
        }
    public:
        thread_pool()
        {
            pthread_mutex_init(&_mtx, nullptr);
            pthread_cond_init(&_cond, nullptr);
        }

        ~thread_pool()
        {
            pthread_mutex_destroy(&_mtx);
            pthread_cond_destroy(&_cond);
        }

        void init()
        {
            pthread_t tid;
            for (int i = 0; i < N; ++i)
            {
                pthread_create(&tid, nullptr, Rountine, (void*)this);
            }
        }

        void push(const T& t)
        {
            lock();
            _q.push(t);
            unlock();
            signal();
        }

        void pop(T* t)
        {
            *t = _q.front();
            _q.pop();
        }
    private:
        std::queue<T> _q;
        pthread_mutex_t _mtx;
        pthread_cond_t _cond;
    };
}